The Quality and Outcomes Framework (QOF) is a voluntary annual reward and incentive programme for all GP practices in England (NHSD, 2021a). The objective of the Quality and Outcomes Framework (QOF) is to improve the quality of care patients are given by rewarding practices for the quality of care they provide to their patients, based on a number of indicators across a range of key areas of clinical care and public health. QOF participation by GP practices is very high, with 96.7% of practices participating in the 2020/21 publication (NHSD, 2021b).
OpenSAFELY is a secure analytics platform for electronic patient records built by our group on behalf of NHS England to deliver urgent academic and operational research during the pandemic (e.g., Curtis et al., 2021; Williamson et al., 2020). Analyses can currently run across all patients’ full pseudonymised primary care records, with patient-level linkage to various sources of secondary care data. The OpenSAFELY-TPP is a Trusted Research Environment (TRE) containing the routine clinical data of 23.4m people, approximately 40% of England’s population.
Currently QOF reports are published at the end of the financial year, and consist of prevalence rates and indicator achievement rates for clinical areas by NHS geographic breakdowns from region to practice. We set out to replicate the indicator logic for a variety of QOF clinical indicators in OpenSAFELY-TPP and describe trends monthly over time, before and during the pandemic. We also describe how the indicators vary between key clinical, regional and demographic subgroups.
High blood pressure is one of the leading risk factors for several diseases (e.g., cardiovascular disease, stroke) worldwide. Research suggests that delays in the management of high blood pressure are associated with worse clinical outcomes, for example acute cardiovascular events, or death (Xu et al., 2015). This report describes the prevalence of people who have a diagnosis of hypertension which has not been subsequently resolved the OpenSAFELY-TPP trusted research environment (TRE).
Using OpenSAFELY-TPP, covering 40% of England’s population, we have evaluated the QOF Hypertension indicators (HYP001, HYP003, HYP007) between 2019-09-01 and 2022-03-31. The codelist used can be found here at OpenSAFELY Codelists.
The study population for the register was defined following the business rules the (v46). For each month within the study period, we have calculated the percentage of registered patients with a diagnosis of hypertension which has not been subsequently resolved. All analytical code and output is available for inspection at the OpenSAFELY GitHub repository.
More details about the rules can be found here. Dashboards presenting the annual targets of all QOF indicators published by NHSD are available here.
The description and rules for the Hypertension Register (HYP001, Version: 46.0) are outlined below:
“Select patients from the specified population who have a diagnosis of hypertension which has not been subsequently resolved.”
| Rule number | Rule | Action if true | Action if false | Rule description or comments |
|---|---|---|---|---|
| 1 | If HYPLAT_DAT ≠ Null AND If HYPRES_DAT = Null |
Select | Reject | Select patients from the specified population who have a diagnosis of hypertension which has not been subsequently resolved. Reject the remaining patients. |
| Cluster name | Description | SNOMED CT |
|---|---|---|
| BP_COD | Blood pressure (BP) recording codes | ^999012731000230108 |
| BPDEC_COD | Codes indicating the patient has chosen not to have blood pressure procedure | ^999012611000230106 |
| HTMAX_COD | Codes for maximal blood pressure (BP) therapy | ^999006651000230109 |
| HYP_COD | Hypertension diagnosis codes | ^999006611000230105 |
| HYPINVITE_COD | Invite for hypertension care review codes | ^999012971000230108 |
| HYPPCADEC_COD | Codes indicating the patient has chosen not to receive hypertension quality indicator care | ^999013091000230102 |
| HYPPCAPU_COD | Codes for hypertension quality indicator care unsuitable for patient | ^999013211000230104 |
| HYPRES_COD | Hypertension resolved codes | ^999006531000230101 |
| Codelist name | Description | Coding system |
|---|---|---|
| Ethnicity | A list of ethnicity codes in use in UK general practice including aggregate grouping at two levels (6 and 16). | CTV3 |
| NHS England Care Homes residential status | This codelists supports analysis of records of people who may reside in a nursing or care home. | SNOMED CT |
| Learning disability (LD) codes | The following codes are used to check for a record of learning disability. | SNOMED CT |
Note that the scaling of the y-axis is different for the total population (0 to 100%) and outputs by subgroup (scaling differs across grouping variables to highlight differences between groups). SMALL NUMBER REDACTION.
df_measures_hyp001 %>%
dplyr::filter(group == "population") %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
geom_segment = TRUE,
legend = "none",
y_label = "Prevalence (%)",
set_y_scale_limits = TRUE)
df_measures_hyp001 %>%
dplyr::filter(group == "population") %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
legend = "none",
x_scale_date_breaks = "4 months",
geom_segment = TRUE,
y_label = "Prevalence (Count)",
set_y_scale_limits = TRUE)
df_measures_hyp001 %>%
dplyr::filter(group == "sex") %>%
dplyr::mutate(category = factor(category,
levels = c("F", "M"),
labels = c("Female", "Male")
)) %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
y_label = "Prevalence (%)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "sex") %>%
dplyr::mutate(category = factor(category,
levels = c("F", "M"),
labels = c("Female", "Male"))) %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "age_band") %>%
dplyr::filter(category != "missing") %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
y_label = "Prevalence (%)")
df_measures_hyp001 %>%
dplyr::filter(group == "age_band") %>%
dplyr::filter(category != "missing") %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)")
df_measures_hyp001 %>%
dplyr::filter(group == "imd") %>%
dplyr::mutate(category = factor(category,
levels = c(1:5),
labels = c("1 - Most deprived", "2", "3", "4", "5 - Least deprived")
)) %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
y_label = "Prevalence (%)")
df_measures_hyp001 %>%
dplyr::filter(group == "imd") %>%
dplyr::mutate(category = factor(category,
levels = c(1:5),
labels = c("1 - Most deprived", "2", "3", "4", "5 - Least deprived")
)) %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)")
df_measures_hyp001 %>%
dplyr::filter(group == "ethnicity") %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (%)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "ethnicity") %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "region") %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (%)") +
scale_colour_brewer(palette = "Set1")
df_measures_hyp001 %>%
dplyr::filter(group == "region") %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)") +
scale_colour_brewer(palette = "Set1")
df_measures_hyp001 %>%
dplyr::filter(group == "learning_disability") %>%
dplyr::mutate(category = factor(category,
levels = c(TRUE, FALSE),
labels = c(
"Record of learning disability",
"No record of learning disability"
)
)) %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (%)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "learning_disability") %>%
dplyr::mutate(category = factor(category,
levels = c(TRUE, FALSE),
labels = c(
"Record of learning disability",
"No record of learning disability"
)
)) %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "care_home") %>%
dplyr::mutate(category = factor(category,
levels = c(TRUE, FALSE),
labels = c(
"Record of positive care home status",
"No record of positive care home status"
)
)) %>%
plot_qof_indicator(
value = value,
y_scale = "percent",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (%)") +
scale_colour_brewer(palette = "Dark2")
df_measures_hyp001 %>%
dplyr::filter(group == "care_home") %>%
dplyr::mutate(category = factor(category,
levels = c(TRUE, FALSE),
labels = c(
"Record of positive care home status",
"No record of positive care home status"
)
)) %>%
plot_qof_indicator(
value = hypertension,
y_scale = "count",
x_scale_date_breaks = "4 months",
y_label = "Prevalence (Count)") +
scale_colour_brewer(palette = "Dark2")